package logbuff

import (
	"encoding/json"
	"fmt"
	"time"

	. "starsine.cn/appTpl/cmn/defs"
	"starsine.cn/appTpl/cmn/logs"
	. "starsine.cn/appTpl/svclog/src/logproc/defs"
	festrung "starsine.cn/appTpl/svclog/src/logproc/fe-strung"
	"starsine.cn/appTpl/svclog/src/logproc/strung"
)

type LogBUff struct {
	size        int
	expire      time.Time
	c           chan LogLine
	quit        chan struct{}
	runOverride func(p *LogBUff)
	appCtxt     *AppContext
}

func CreateLogBUff(appCtxt *AppContext, size int, runOverride func(*LogBUff)) *LogBUff {
	p := new(LogBUff)

	p.size = size

	p.expire = time.Now().Add(time.Minute)
	p.c = make(chan LogLine, p.size)
	p.quit = make(chan struct{})
	p.runOverride = runOverride
	p.appCtxt = appCtxt

	if p.runOverride != nil {
		go p.runOverride(p)
	} else {
		go run(p)
	}

	return p
}

func (p *LogBUff) Close() {
	close(p.quit)
}

func (p *LogBUff) In(line LogLine) error {

	select {
	case p.c <- line:
		p.expire = time.Now().Add(time.Minute)

	default:
		return fmt.Errorf("LogBUff in fail, current buff size = %d", len(p.c))
	}

	return nil
}

func (p *LogBUff) OutAsync() *LogLine {

	select {
	case line := <-p.c:
		return &line
	default:
		return nil
	}
}

func (p *LogBUff) OutSync() <-chan LogLine {
	return p.c
}

func (p *LogBUff) Quit() <-chan struct{} {
	return p.quit
}

func (p *LogBUff) AppContext() *AppContext {
	return p.appCtxt
}

func (p *LogBUff) IsExpired() bool {
	return !p.expire.After(time.Now())
}

func run(p *LogBUff) {
	logs.Infof("LogBUff.run routine start")
	p.appCtxt.Wg.Add(1)
	defer func() {
		logs.Warnf("LogBUff.run routine exit")
		p.appCtxt.Wg.Done()
	}()

	for {
		select {
		case line := <-p.c:
			v := SvcLogLine{}
			err := json.Unmarshal(line.Data, &v)
			if err != nil {
				logs.ErrorfWith("LogBUff", "SvcLogLine Unmarshal fail, err: %s", err)
				continue
			}

			s := strung.Get(line.InstanceID)
			s.Join(&v)

			// emit to fe
			fs := festrung.Get(line.InstanceID)
			if fs != nil {
				fs.Join(&v)
			}

		case <-p.quit:
			logs.WarnfWith("LogBUff", "LogBUff routine quit")
			return

		case <-p.appCtxt.Ctx.Done():
			logs.Warnf("LogBUff.run  LogBUff ctxt done to quit")
			return
		}
	}
}
